package org.okcjug.december;

import static org.junit.Assert.*;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;
import java.util.regex.Pattern;

import org.junit.Test;
import org.okcjug.december.model.Movie;
import org.okcjug.december.util.MovieFactory;

public class CollectionsFun {
	private static final Pattern SPLIT_ON_WHITESPACE = java.util.regex.Pattern.compile("[\\W]+");
	private Set<Movie> movies = new TreeSet<Movie>();
	private List<Movie> movieList;
	private static String NEWLINE = System.getProperty("line.separator");
	
	public CollectionsFun(){
		this.movies.add(MovieFactory.getClerks());
		this.movies.add(MovieFactory.getDrStrangelove());		
		this.movies.add(MovieFactory.getKentuckyFriedMovie());
		this.movies.add(MovieFactory.getMadMaxThunderDome());
		this.movies.add(MovieFactory.getMysteryMen());
		this.movies.add(MovieFactory.getRockyIII());
		this.movies.add(MovieFactory.getTopSecret());
		this.movies.add(MovieFactory.getSierraMadre());
		this.movies.add(MovieFactory.getPatton());
		this.movies.add(MovieFactory.getTheJerk());
		this.movies.add(MovieFactory.getSummerOfSam());
		
		movieList = new ArrayList<Movie>(movies);
	}
		
	//Collections.sort
	@Test
	public void orderByNumberOfQuotesDescending(){
		Collections.sort(movieList, new NumQuotesDescComparator());
		int lastNumQuotes = 0;
		for(Movie m : movieList){
			int numQuotes = m.getQuotes().size();
			assertTrue(lastNumQuotes <= numQuotes);
			System.out.println(m.getMovie() + " has " + numQuotes + " quote(s)");
			lastNumQuotes = numQuotes;			
		}
	}
	
//	//Collections.binarySearch
//	@Test
//	public void binarySearch(){
//		List<Movie> movieListCopy = new ArrayList<Movie>(movies);
//		//Collections.sort(movieListCopy, new MovieTitleLengthComparator());
//		
//		int position = Collections.binarySearch(movieListCopy, MovieFactory.getTopSecret());
//		Movie topSecret = movieListCopy.get(position);
//		assertEquals(topSecret, MovieFactory.getTopSecret());
//		System.out.println(topSecret);
//	}
//	
//	//Collections.reverseOrder
//	@Test
//	public void orderByNumberOfQuotesAscending(){
//		Comparator<Movie> reverse 
//			= Collections.reverseOrder(new NumQuotesDescComparator());
//		Collections.sort(movieList, reverse);
//		int lastNumQuotes = 4;
//		for(Movie m : movieList){
//			int numQuotes = m.getQuotes().size();
//			assertTrue(lastNumQuotes >= numQuotes);
//			System.out.println(m.getMovie() + " has " + numQuotes + " quote(s)");
//			lastNumQuotes = numQuotes;			
//		}
//	}
	//Collections.unmodifiable
	@Test
	public void cantModifyMe(){
//		List<Movie> copy = new ArrayList<Movie>(movieList);
//		List<Movie> unmodifiable = Collections.unmodifiableList(copy);
//		int size = copy.size();
//		try{
//			unmodifiable.add(MovieFactory.getClerks());
//			fail("Shouldn't be able to modify the list");
//		}catch(UnsupportedOperationException e){
//			//supposed to fail
//		}
//		try{
//			unmodifiable.add(MovieFactory.getClerks());
//			fail("Shouldn't be able to modify the list");
//		}catch(UnsupportedOperationException e){
//			//supposed to fail
//		}
//		
//		//but not so fast
//		copy.add(MovieFactory.getClerks());
//		assertTrue(size == unmodifiable.size());
	}
	
	@Test
	public void checkedCollection(){
//		List<Integer> numbers = new ArrayList<Integer>();
//		numbers.add(new Integer(23));
		
//		List copy = numbers;
//		copy.add("this isn't an Integer");
//		
//		Iterator it = copy.iterator();
//		int sum = 0;
//		while(it.hasNext()){
//			sum += (Integer) it.next();
//		}
		
//		List<Integer> numbers2 = new ArrayList<Integer>();
//		numbers2.add(new Integer(23));
//		
//		List<Integer> checkedNumbers
//			= Collections.checkedList(new ArrayList<Integer>(), Integer.class);
//		List copy2 = checkedNumbers;
//		try{
//			copy2.add("Not a number");
//			fail("Should throw a class cast exception");
//		}catch(ClassCastException e){
//			//should happen
//		}
		
	}
	
	public Integer getNumberOfQuotes(){
		Integer cnt = Integer.valueOf(0);
		for(Movie movieQuotes : movies){
			cnt += movieQuotes.getQuotes().size();
		}
		return cnt; 
	}
	
	public Integer getNumberOfMovies(){
		return movies.size();
	}
	
	public String getLameMoviesForQuotes(){
		StringBuilder movieNames = new StringBuilder();
		for(Movie m : movies){
			if(m.getQuotes().isEmpty()){				
				if(movieNames.length() == 0){
					movieNames.append(m.getMovie());
				}else{
					movieNames.append(", " + m.getMovie());
				}
			}
		}
		return movieNames.toString();
	}
	
	public String orderByMovieTitleLength(){
		Collections.sort(movieList, new LargestMovieTitleComparator());
		return buildMovieQuoteString(movieList);
	}
	
	public String orderByWordsInQuote(Movie mq){
		List<String> quotes = mq.getQuotes();
		Collections.sort(quotes, new NumberOfWordsComparator());
		mq.setQuotes(quotes);
		return buildMovieQuoteString(mq);
	}
	
	public static void main(String...args){
		CollectionsFun c = new CollectionsFun();
//		System.out.println("Number of movie quotes: " + c.getNumberOfQuotes());
//		System.out.println("Number of movies: " + c.getNumberOfMovies());
//		System.out.println("Movies to " +
//				"lame to have any good quotes: " + c.getLameMoviesForQuotes());
//		System.out.println(c.orderByNumberOfQuotesDescending());
//		System.out.println("----------------------------------------------");
//		System.out.println(c.orderByMovieTitleLength());
//		System.out.println(c.orderByWordsInQuote(MovieFactory.getMysteryMen()));
	}
	
	private String buildMovieQuoteString(List<Movie> quotes){
		List<Movie> mQuotes = new ArrayList<Movie>(quotes);
		StringBuilder sb = new StringBuilder();
		for(Movie mq : mQuotes){
			sb.append(buildMovieQuoteString(mq));
		}
		return sb.toString();
	}
	
	private String buildMovieQuoteString(Movie mq){
		StringBuilder sb = new StringBuilder();
		sb.append(mq.getMovie() + ":" + NEWLINE + NEWLINE);
		for(String quote: mq.getQuotes()){
			sb.append(quote + NEWLINE + NEWLINE);
		}
		sb.append("****************************************" + NEWLINE);
		return sb.toString();
	}
	
	private class NumQuotesDescComparator implements Comparator<Movie>, Serializable{

		private static final long serialVersionUID = -935561663774164465L;

		public int compare(Movie o1, Movie o2) {
			int result = o1.getQuotes().size() - o2.getQuotes().size();
			if(result == 0){
				return o1.getMovie().compareTo(o2.getMovie());
			}
			return result;
		}
	
	}
	
	private class LargestMovieTitleComparator implements Comparator<Movie>, Serializable{

		private static final long serialVersionUID = -935561663774164465L;

		public int compare(Movie o1, Movie o2) {
			String o1Movie = o1.getMovie().trim();
			String o2Movie = o2.getMovie().trim();
			if(o1Movie.length() > o2Movie.length()){
				return -1;
			}else if(o1Movie.length() < o2Movie.length()){
				return 1;
			}
			return o1Movie.compareTo(o2Movie);
		}	
	}
	
	private class NumberOfWordsComparator implements Comparator<String>, Serializable{

		private static final long serialVersionUID = -935561663774164465L;

		public int compare(String s1, String s2) {
			return numOfWords(s1).compareTo(numOfWords(s2));
		}	
	}
	
	private class MovieTitleLengthComparator implements Comparator<Movie>, Serializable{

		@Override
		public int compare(Movie o1, Movie o2) {
			return o1.getMovie().compareTo(o2.getMovie());
		}
		
	}
	
	private Integer numOfWords(String quote){
		String[] words = SPLIT_ON_WHITESPACE.split(quote);
		return words.length;
	}
}